﻿using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Net;
using System.Globalization;
using System.Collections;

namespace Server {
	public partial class Form1 : Form {
		private ServiceController serviceController;
		private Dictionary<String, WifiSecurityType> wifiSecurityTypeDescription;
		private Dictionary<String, WifiEncryptionType> wifiEncryptionTypeDescription;

		delegate void BeginInvokeDelegate();

		public Form1() {
			InitializeComponent();
			Logger.setTextBox(log);

			serviceController = new ServiceController("localhost", 8000);
			wifiSecurityTypeDescription = new Dictionary<String, WifiSecurityType>();
			wifiSecurityTypeDescription.Add("Open", WifiSecurityType.Open);
			wifiSecurityTypeDescription.Add("WEP", WifiSecurityType.WEP);
			wifiSecurityTypeDescription.Add("WPA", WifiSecurityType.WPA_PSK);
			wifiSecurityTypeDescription.Add("WPA2", WifiSecurityType.WPA2_PSK);
			wifiEncryptionTypeDescription = new Dictionary<String, WifiEncryptionType>();
			wifiEncryptionTypeDescription.Add("Disable", WifiEncryptionType.none);
			wifiEncryptionTypeDescription.Add("WEP", WifiEncryptionType.WEP);
			wifiEncryptionTypeDescription.Add("TKIP", WifiEncryptionType.TKIP);
			wifiEncryptionTypeDescription.Add("AES", WifiEncryptionType.AES);

			this.HandleCreated += (Object o, EventArgs e) => {
				Logger.Log("Application running...");

				// adding services
				Logger.Debug("Creating service controller and adding services...");
				serviceController.AddService(typeof(ServiceModelServer));

				Logger.Debug("Scanning for network interfaces...");
				foreach (NetworkInterface netif in NetworkInterface.GetAllNetworkInterfaces()) {
					if (netif.OperationalStatus != OperationalStatus.Up || netif.NetworkInterfaceType == NetworkInterfaceType.Loopback) {
						continue;
					}
					if (null != netif.GetIPProperties().UnicastAddresses && netif.GetIPProperties().UnicastAddresses.Count > 0) {
						foreach (UnicastIPAddressInformation unicast in netif.GetIPProperties().UnicastAddresses) {
							if (unicast.Address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) {
								continue;
							}

							String mask;
							if (null == unicast.IPv4Mask) {
								mask = "n/a";
							} else {
								mask = unicast.IPv4Mask.ToString();
							}
							Logger.Info(netif.NetworkInterfaceType + " (" + netif.Name + "): " + unicast.Address + "/" + mask + " (" + (Math.Round(netif.Speed / 1000000.0, 2)) + "Mbps)");
						}
					} else {
						Logger.Info(netif.NetworkInterfaceType + " (" + netif.Name + "): interface without configured address");
					}
				}
			};

			// event on add/delete node from server
			Server.Instance.NodeListChanged += (o, e) => {
				Logger.Info("Node list changed");

				nodeList.BeginInvoke(new BeginInvokeDelegate(() => {
					lock (Server.Instance.SyncRoot) {
						Guid[] nodes = Server.Instance.GetList();

						List<Guid> selectedItemsTags = new List<Guid>();
						foreach (ListViewItem item in nodeList.SelectedItems) {
							selectedItemsTags.Add((Guid) item.Tag);
						}

						nodeList.SelectedIndices.Clear();
						nodeList.Items.Clear();
						testControlTypeEveryoneToOneNode.Items.Clear();

						foreach (Guid guid in nodes) {
							Node node = null;
							try {
								node = Server.Instance.NodeControl(guid).Node;
							} catch (IndexOutOfRangeException) {
								Logger.Critical(String.Format("Node {0} doesn't exist!", guid));
								continue;
							}
							if (null != node) {
								ListViewItem item = new ListViewItem(node.Ip.ToString());
								item.SubItems.Add(guid.ToString());
								item.Tag = guid;
								nodeList.Items.Add(item);

								if (selectedItemsTags.Count(element => element.Equals(guid)) > 0) {
									nodeList.SelectedIndices.Add(nodeList.Items.IndexOf(item));
								}

								testControlTypeEveryoneToOneNode.Items.Add(node);
							}
						}
						nodeListCount.Text = nodeList.Items.Count.ToString();
					}
				}));
			};


			wifiControlSecurityType.Items.Clear();
			foreach (String securityType in wifiSecurityTypeDescription.Keys) {
				wifiControlSecurityType.Items.Add(securityType);
			}
	
			wifiControlSecurityType.SelectedIndex = 0;
			wifiControlEncryptionType.SelectedIndex = 0;
		}

		private void serverControlStartStop_CheckedChanged(object sender, EventArgs e) {
			if (!serverControlStartStop.Enabled) {
				return;
			}

			lock (nodeList) {
				nodeList.SelectedIndices.Clear();
				nodeList.Items.Clear();
			}

			BackgroundWorker backgroundWorker = new BackgroundWorker();

			serverConfigPort.Enabled = false;
			serverControlStartStop.Enabled = false;

			if (serviceController.IsRunning()) {
				serverControlStartStop.Text = "Stopping...";
			} else {
				serverControlStartStop.Text = "Running...";
			}

			backgroundWorker.DoWork += (o, ev) => {
				if (!serverControlStartStop.Checked && null != serviceController && serviceController.IsRunning()) {
					Logger.Info("Stopping server...");
					serviceController.Stop();
				} else {
					Logger.Info("Starting server...");

					serviceController.SetPort(serverConfigPort.Value);
					serviceController.Start();
				}
			};
			backgroundWorker.RunWorkerCompleted += (o, ev) => {
				if (serviceController.IsRunning()) {
					serverControlStartStop.Checked = true;
					serverControlStartStop.Text = "Stop";
					nodeListRefreshTimer.Start();
					Logger.Info("Server has started...");
				} else {
					serverControlStartStop.Checked = false;
					serverConfigPort.Enabled = true;
					serverControlStartStop.Text = "Start";
					nodeListRefreshTimer.Stop();
					Logger.Info("Server has stopped...");
				}
				serverControlStartStop.Enabled = true;
			};
			backgroundWorker.RunWorkerAsync();
		}

		private void wifiControlConnect_Click(object sender, EventArgs e) {
			BackgroundWorker backgroundWorker;
			WifiSecurityType securityType = (WifiSecurityType) wifiSecurityTypeDescription.First(element => element.Key.Equals(wifiControlSecurityType.SelectedItem)).Value;
			WifiEncryptionType encryptionType = (WifiEncryptionType) wifiEncryptionTypeDescription.First(element => element.Key.Equals(wifiControlEncryptionType.SelectedItem)).Value;

			// kontrola długości hasła
			if (securityType == WifiSecurityType.WEP) {
				if (wifiControlSecurityKey.Text.Length != 5 && wifiControlSecurityKey.Text.Length != 13) {
					MessageBox.Show("Security key for WEP should have 5 or 13 ASCII chars", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
					return;
				}
			} else if (securityType == WifiSecurityType.WPA2_PSK || securityType == WifiSecurityType.WPA_PSK) {
				if (wifiControlSecurityKey.Text.Length < 8 || wifiControlSecurityKey.Text.Length > 63) {
					MessageBox.Show("Security key for WPA/WPA2 should have minimum 8 and maximum 63 ASCII chars", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
					return;
				}
			}

			wifiControlConnect.Enabled = false;
			wifiControlDisconnect.Enabled = false;

			lock (nodeList) {
				int connections = nodeList.SelectedItems.Count;

				foreach (ListViewItem node in nodeList.SelectedItems) {
					backgroundWorker = new BackgroundWorker();
					backgroundWorker.DoWork += (o, ea) => {
						try {
							Server.Instance.NodeControl((Guid) ea.Argument).WifiConnect(wifiControlSSID.Text, securityType, encryptionType, wifiControlSecurityKey.Text);
						} catch (IndexOutOfRangeException) {
							Logger.Critical(String.Format("Node {0} doesn't exist!", (Guid) ea.Argument));
						}
					};

					backgroundWorker.RunWorkerCompleted += (o, ea) => {
						if (0 == Interlocked.Decrement(ref connections)) {
							wifiControlConnect.Enabled = true;
							wifiControlDisconnect.Enabled = true;
						}
					};

					backgroundWorker.RunWorkerAsync(node.Tag);
				}
			}
		}

		private void wifiControlDisconnect_Click(object sender, EventArgs e) {
			BackgroundWorker backgroundWorker;

			wifiControlDisconnect.Enabled = false;
			wifiControlConnect.Enabled = false;

			lock (nodeList) {
				int connections = nodeList.SelectedItems.Count;

				foreach (ListViewItem node in nodeList.SelectedItems) {
					backgroundWorker = new BackgroundWorker();
					backgroundWorker.DoWork += (o, ea) => {
						try {
							Server.Instance.NodeControl((Guid) ea.Argument).WifiDisconnect();
						} catch (IndexOutOfRangeException) {
							Logger.Critical(String.Format("Node {0} doesn't exist!", (Guid) ea.Argument));
						}
					};

					backgroundWorker.RunWorkerCompleted += (o, ea) => {
						if (0 == Interlocked.Decrement(ref connections)) {
							wifiControlDisconnect.Enabled = true;
							wifiControlConnect.Enabled = true;
						}
					};

					backgroundWorker.RunWorkerAsync(node.Tag);
				}
			}
		}

		private void nodeListSelectAll_Click(object sender, EventArgs e) {
			lock (nodeList) {
				nodeList.SelectedIndices.Clear();
				foreach (ListViewItem item in nodeList.Items) {
					nodeList.SelectedIndices.Add(item.Index);
				}
			}
		}

		private void wifiControlSecurityType_SelectedIndexChanged(object sender, EventArgs e) {
			WifiSecurityType securityType = (WifiSecurityType) wifiSecurityTypeDescription.First(element => element.Key.Equals(wifiControlSecurityType.SelectedItem)).Value;
			wifiControlEncryptionType.Items.Clear();

			wifiControlEncryptionType.Enabled = true;
			wifiControlSecurityKey.Enabled = true;
			wifiControlHideChars.Enabled = true;

			switch (securityType) {
				default:
				case WifiSecurityType.Open:
					wifiControlEncryptionType.Items.Add(wifiEncryptionTypeDescription.First(element => element.Value == WifiEncryptionType.none).Key);
					wifiControlEncryptionType.Enabled = false;
					wifiControlSecurityKey.Enabled = false;
					wifiControlHideChars.Enabled = false;
					break;
				case WifiSecurityType.WEP:
					wifiControlEncryptionType.Items.Add(wifiEncryptionTypeDescription.First(element => element.Value == WifiEncryptionType.WEP).Key);
					wifiControlEncryptionType.Enabled = false;
					wifiControlSecurityKey.MaxLength = 13;
					break;
				case WifiSecurityType.WPA2_PSK:
				case WifiSecurityType.WPA_PSK:
					wifiControlEncryptionType.Items.Add(wifiEncryptionTypeDescription.First(element => element.Value == WifiEncryptionType.TKIP).Key);
					wifiControlEncryptionType.Items.Add(wifiEncryptionTypeDescription.First(element => element.Value == WifiEncryptionType.AES).Key);
					wifiControlSecurityKey.MaxLength = 63;
					break;

			}
			wifiControlEncryptionType.SelectedIndex = 0;
		}

		private void wifiControlHideChars_CheckedChanged(object sender, EventArgs e) {
			wifiControlSecurityKey.UseSystemPasswordChar = wifiControlHideChars.Checked;
		}

		private void nodeList_ColumnClick(object sender, ColumnClickEventArgs e) {
			// TODO Sorting po kolumnach
			//e.Column
		}

		private void nodeList_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) {
			nodeLog.Clear();

			wifiControl.Enabled = testControl.Enabled = testTests.Enabled = (nodeList.SelectedIndices.Count > 0);
			wifiStatus.Enabled = wifiStatusRefresh.Enabled = (nodeList.SelectedIndices.Count == 1);
			wifiStatusAutorefresh.Checked = false;

			testIperfServers.Maximum = nodeList.SelectedIndices.Count;
			if (testIperfServers.Maximum > 0) {
				testIperfServers.Minimum = 1;
			} else {
				testIperfServers.Maximum = 0;
			}
			testIperfServers_ValueChanged(testIperfServers, EventArgs.Empty);

			statusControlDownloadLogs.Enabled = true;
			statusControlCloseNodes.Enabled = true;
			if (nodeList.SelectedIndices.Count == 1) {
				if (tabControl.SelectedTab == tabLog) {
					logRefresh_Click(logRefresh, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
				}

				Guid guid = (Guid) nodeList.SelectedItems[0].Tag;
				statusNetworkGuid.Text = guid.ToString();
				try {
					statusNetworkIpv4.Text = Server.Instance.GetNode(guid).Ip + ":" + Server.Instance.GetNode(guid).Port;
				} catch (IndexOutOfRangeException) {
					statusNetworkIpv4.Text = "n/a";
				}
			} else {
				if (nodeList.SelectedIndices.Count == 0) {
					statusControlDownloadLogs.Enabled = false;
					statusControlCloseNodes.Enabled = false;
				}
				wifiStatusSSID.Text = "Unknown";
				wifiStatusOperationalStatus.Text = "Down";
				wifiStatusSignal.Value = 0;
				wifiStatusSpeed.Text = "Unknown";
				wifiStatusIPv4.Text = "Unknown";
				wifiStatusBytesReceived.Text = "0";
				wifiStatusBytesSent.Text = "0";
				wifiStatusInterfaceState.Text = "Disconnected";
				wifiStatusIncomingPacketsDiscarded.Text = "0";
				wifiStatusIncomingPacketsWithErrors.Text = "0";
				wifiStatusOutgoingPacketsDiscarded.Text = "0";
				wifiStatusOutgoingPacketsWithErrors.Text = "0";
				wifiStatusOutputQueueLength.Text = "0";
				statusNetworkGuid.Text = "n/a";
				statusNetworkIpv4.Text = "n/a";
			}
		}

		private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
			serviceController.Stop();
		}

		private void wifiStatusRefresh_Click(object sender, EventArgs e) {
			if (nodeList.SelectedIndices.Count == 1) {

				BackgroundWorker bw = new BackgroundWorker();
				wifiStatusRefresh.Enabled = false;
				wifiStatusRefreshTimer.Enabled = false;

				bw.DoWork += (o, ea) => {
					try {
						ea.Result = Server.Instance.NodeControl((Guid) ea.Argument).WifiGetConnectionState();
					} catch (IndexOutOfRangeException) {
						Logger.Critical(String.Format("Node {0} doesn't exist!", (Guid) ea.Argument));
						ea.Cancel = true;
					}
				};

				bw.RunWorkerCompleted += (o, ea) => {
					if (ea.Cancelled) {
						return;
					}
					// jeśli w międzyczasie zablokowano formatkę, zapomnij
					// jeśli zmieniono zaznaczenie - zapomnij
					if (!wifiStatus.Enabled || nodeList.SelectedItems.Count != 1 || nodeList.SelectedItems[0].Tag != wifiStatus.Tag) {
						return;
					}

					wifiStatusRefresh.Enabled = true;
					wifiStatusRefreshTimer.Enabled = wifiStatusAutorefresh.Checked;

					if (null != ea.Error) {
						Logger.Warning("Wifi status refresh worker raises error: " + ea.Error.Message);
						return;
					}
					WifiConnectionState state = (WifiConnectionState) ea.Result;
					wifiStatusSSID.Text = state.ConnectionSSID;
					wifiStatusOperationalStatus.Text = state.ConnectionOperationalStatus;
					int signal = state.ConnectionRSSI + 100;
					if (signal > 50) {
						Logger.Info("RSSI above value -50 (RSSI = " + signal + ", weird stuff");
						signal = 50;
					}
					wifiStatusSignal.Value = signal;
					wifiStatusSpeed.Text = String.Format("{0:f2} Mbps", state.ConnectionSpeed / (1000*1000));

					wifiStatusIPv4.Text = (new IPAddress(state.ConnectionIPv4UnicastAddress)).ToString() + "/" + (new IPAddress(state.ConnectionIPv4UnicastMask)).ToString();
					wifiStatusBytesReceived.Text = state.ConnectionIPv4StatisticsBytesReceived.ToString("N0", CultureInfo.InvariantCulture);
					wifiStatusBytesSent.Text = state.ConnectionIPv4StatisticsBytesSent.ToString("N0", CultureInfo.InvariantCulture);

					wifiStatusInterfaceState.Text = state.InterfaceState;
					wifiStatusIncomingPacketsDiscarded.Text = state.ConnectionIPv4StatisticsIncomingPacketsDiscarded.ToString();
					wifiStatusIncomingPacketsWithErrors.Text = state.ConnectionIPv4StatisticsIncomingPacketsWithErrors.ToString();
					wifiStatusOutgoingPacketsDiscarded.Text = state.ConnectionIPv4StatisticsOutgoingPacketsDiscarded.ToString();
					wifiStatusOutgoingPacketsWithErrors.Text = state.ConnectionIPv4StatisticsOutgoingPacketsWithErrors.ToString();
					wifiStatusOutputQueueLength.Text = state.ConnectionIPv4StatisticsOutputQueueLength.ToString();
				};

				wifiStatus.Tag = nodeList.SelectedItems[0].Tag;
				bw.RunWorkerAsync(wifiStatus.Tag);
			}
		}

		private void wifiStatusAutorefreshTime_ValueChanged(object sender, EventArgs e) {
			wifiStatusRefreshTimer.Interval = (int) ((NumericUpDown) sender).Value * 1000;
			wifiStatusRefreshTimer.Start();
		}

		private void wifiStatusAutorefresh_CheckedChanged(object sender, EventArgs e) {
			wifiStatusRefreshTimer.Enabled = ((CheckBox) sender).Checked;
		}

		private void wifiStatusRefreshTimer_Tick(object sender, EventArgs e) {
			if (!wifiStatusAutorefresh.Enabled) {
				// zmieniono node...
				wifiStatusAutorefresh.Checked = false;
				return;
			}
			if (!wifiStatusRefresh.Enabled) {
				// trwa aktualizacja...
				return;
			}

			wifiStatusRefresh_Click(wifiStatusRefresh, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
		}

		private void testPingStart_Click(object sender, EventArgs e) {
			BackgroundWorker backgroundWorker;
			Dictionary<Guid, String> servers = new Dictionary<Guid, String>();
			String testUniqId = "ping/" + DateTime.Now.ToString("HHmmssff");
			
			lock (nodeList) {
				int connections = 0;

				if (testControlTypePeerToPeer.Checked && nodeList.SelectedItems.Count < 2) {
					MessageBox.Show("To perform p2p ping test you need minimum 2 nodes", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
					return;
				}

				if (testControlTypeExternal.Checked) {
					servers.Add(Guid.Empty, testControlTypeExternalAddress.Text);
				} else if (testControlTypeEveryoneToOne.Checked) {
					Node node = (Node) testControlTypeEveryoneToOneNode.SelectedItem;
					servers.Add(node.Name, node.Ip.ToString());
				} else {
					for (int i = 0; i < nodeList.SelectedItems.Count; ++i) {
						ListViewItem node = nodeList.SelectedItems[i];
						Guid nodeGuid = (Guid) node.Tag;
						try {
							String ip = Server.Instance.GetNode(nodeGuid).Ip.ToString();
							servers.Add(nodeGuid, ip);
						} catch (IndexOutOfRangeException) {
							Logger.Warning("Can\'t add server to server list!");
						}
					}
				}

				testPingStart.Enabled = false;
				connections = nodeList.SelectedItems.Count;
				for (int i = 0; i < nodeList.SelectedItems.Count; ++ i) {
					ListViewItem node = nodeList.SelectedItems[i];
					backgroundWorker = new BackgroundWorker();

					KeyValuePair<Guid, String> pongNode = servers.ElementAt((i + 1) % servers.Count);

					backgroundWorker.DoWork += (o, ea) => {
						Object[] eaobjarray = (Object[]) ea.Argument;
						KeyValuePair<Guid, String> ping = (KeyValuePair<Guid, String>) eaobjarray[0];
						KeyValuePair<Guid, String> pong = (KeyValuePair<Guid, String>) eaobjarray[1];

						if (!pong.Key.Equals(Guid.Empty)) {
							try {
								WifiConnectionState clientState = Server.Instance.NodeControl(pong.Key).WifiGetConnectionState();
								if (null != clientState.ConnectionIPv4UnicastAddress && clientState.InterfaceState.Equals("Connected") && clientState.ConnectionOperationalStatus.Equals("Up")) {
									IPAddress ip = new IPAddress(clientState.ConnectionIPv4UnicastAddress);
									if (!ip.Equals(IPAddress.None)) {
										pong = new KeyValuePair<Guid, string>(pong.Key, ip.ToString());
									}
								}
							} catch (IndexOutOfRangeException) {
								Logger.Warning(String.Format("Node {0} runs away!", pong.Key));
								return;
							}
						}

						Logger.Test(String.Format("Ping test Request from {0} [#{1}] to {2} [#{3}]", ping.Value, ping.Key, pong.Value, pong.Key));
						try {
							Server.Instance.NodeControl(ping.Key).TestPing(testUniqId, pong.Value, (int) testPingCount.Value, (int) testPingInterval.Value, (int) testPingTimeout.Value, !testPingFloodPing.Checked);
						} catch (IndexOutOfRangeException) {
							Logger.Critical(String.Format("Node {0} doesn't exist!", ping.Key));
						}
					};

					backgroundWorker.RunWorkerCompleted += (o, ea) => {
						if (0 == Interlocked.Decrement(ref connections)) {
							testPingStart.Enabled = true;
						}
					};

					try {
						backgroundWorker.RunWorkerAsync(new Object[] {
							new KeyValuePair<Guid, String>((Guid) node.Tag, Server.Instance.GetNode((Guid) node.Tag).Ip.ToString()),
							pongNode
						});
					} catch (IndexOutOfRangeException) {
						Logger.Warning("Can\'t run ping test!");
					}
				}
			}
		}

		private void testPingFloodPingLabel_Click(object sender, EventArgs e) {
			testPingFloodPing.Checked = !testPingFloodPing.Checked;
		}

		private void testPingCountLabel_Click(object sender, EventArgs e) {
			testPingCount.Focus();
		}

		private void testPingIntervalLabel_Click(object sender, EventArgs e) {
			testPingInterval.Focus();
		}

		private void testPingTimeoutLabel_Click(object sender, EventArgs e) {
			testPingTimeout.Focus();
		}

		private void wifiControlSSIDLabel_Click(object sender, EventArgs e) {
			wifiControlSSID.Focus();
		}

		private void wifiControlSecurityTypeLabel_Click(object sender, EventArgs e) {
			wifiControlSecurityType.Focus();
		}

		private void wifiControlEncryptionTypeLabel_Click(object sender, EventArgs e) {
			wifiControlEncryptionType.Focus();
		}

		private void wifiControlSecurityKeyLabel_Click(object sender, EventArgs e) {
			wifiControlSecurityKey.Focus();
		}

		private void wifiStatusAutorefreshTimeLabel_Click(object sender, EventArgs e) {
			wifiStatusAutorefreshTime.Focus();
		}

		private void logRefresh_Click(object sender, EventArgs e) {
			BackgroundWorker bw = new BackgroundWorker();

			if (!logRefresh.Enabled) {
				return;
			}

			if (nodeList.SelectedItems.Count != 1) {
				MessageBox.Show("You can download node logs, if and only if exactly one node is selected", "Exclamation", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
				return;
			}

			logRefresh.Enabled = false;

			bw.DoWork += (o, ea) => {
				try {
					ea.Result = Server.Instance.NodeControl((Guid) ea.Argument).GetLogs(nodeLog.Lines.Length);
				} catch (IndexOutOfRangeException) {
					Logger.Critical(String.Format("Node {0} doesn't exist!", (Guid) ea.Argument));
					ea.Cancel = true;
				}
			};

			bw.RunWorkerCompleted += (o, ea) => {
				logRefresh.Enabled = true;
				if (ea.Cancelled) {
					return;
				}
				if (null == ea.Error) {
					ArrayList lines = new ArrayList(nodeLog.Lines);
					lines.AddRange((String[]) ea.Result);
					nodeLog.Lines = (String[]) lines.ToArray(typeof(String));
				}
				nodeLog.SelectionStart = nodeLog.TextLength;
				nodeLog.ScrollToCaret();
			};

			bw.RunWorkerAsync(nodeList.SelectedItems[0].Tag);
		}

		private void tabControl_SelectedIndexChanged(object sender, EventArgs e){
			if (nodeList.SelectedIndices.Count == 1 && tabControl.SelectedTab == tabLog) {
				logRefresh_Click(logRefresh, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
			}
		}

		private void testControlTypeExternal_CheckedChanged(object sender, EventArgs e) {
			if (testControlTypeExternal.Checked) {
				testControlTypeExternalAddress.Focus();
			}
		}

		private void testControlTypeEveryoneToOne_CheckedChanged(object sender, EventArgs e) {
			if (testControlTypeEveryoneToOne.Checked) {
				testControlTypeEveryoneToOneNode.Focus();
			}
		}

		private void testControlTypeEveryoneToOneNode_Enter(object sender, EventArgs e) {
			testControlTypeEveryoneToOne.Checked = true;
		}

		private void testControlTypeExternalAddress_Enter(object sender, EventArgs e) {
			testControlTypeExternal.Checked = true;
		}

		private void testControlIgnoreOrphans_Enter(object sender, EventArgs e) {
			testControlTypePeerToPeer.Checked = true;
		}

		private void nodeListRefreshTimer_Tick(object sender, EventArgs e) {
			BackgroundWorker backgroundWorker;

			lock (nodeList) {
				int connections = nodeList.Items.Count;
				if (connections <= 0) {
					return;
				}

				nodeListRefreshTimer.Stop();

				foreach (ListViewItem node in nodeList.Items) {
					backgroundWorker = new BackgroundWorker();
					backgroundWorker.DoWork += (o, ea) => {
						try {
							Server.Instance.NodeControl((Guid) ea.Argument).TestConnection();
						} catch (IndexOutOfRangeException) {
							Logger.Critical(String.Format("Node {0} doesn't exist!", (Guid) ea.Argument));
						}
					};

					backgroundWorker.RunWorkerCompleted += (o, ea) => {
						if (0 == Interlocked.Decrement(ref connections)) {
							nodeListRefreshTimer.Start();
						}
					};

					backgroundWorker.RunWorkerAsync(node.Tag);
				}
			}
		}

		private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
			Logger.Info("Application has stopped");
			FileLogger.SaveLog(Guid.Empty, Logger.GetLogs());
		}

		private void statusControlDownloadLogs_Click(object sender, EventArgs e) {
			BackgroundWorker backgroundWorker;

			lock (nodeList) {
				int connections = nodeList.SelectedItems.Count;
				if (connections <= 0) {
					return;
				}

				statusControlDownloadLogs.Enabled = false;

				foreach (ListViewItem node in nodeList.SelectedItems) {
					backgroundWorker = new BackgroundWorker();
					backgroundWorker.DoWork += (o, ea) => {
						Guid guid = (Guid) ea.Argument;
						try {
							FileLogger.SaveLog(guid, Server.Instance.NodeControl(guid).GetLogs());
						} catch (IndexOutOfRangeException) {
							Logger.Critical(String.Format("Node {0} doesn't exist!", guid));
						}
					};

					backgroundWorker.RunWorkerCompleted += (o, ea) => {
						if (0 == Interlocked.Decrement(ref connections)) {
							statusControlDownloadLogs.Enabled = true;
						}
					};

					backgroundWorker.RunWorkerAsync(node.Tag);
				}
			}
		}

		private void testIperfStart_Click(object sender, EventArgs e) {
			BackgroundWorker backgroundWorker, bw;
			Dictionary<Guid, String> servers = new Dictionary<Guid, String>();

			String serverCommand = String.Format("iperf.exe -s -p {0} -fm -B {{0}}", testIperfPort.Value);
			String clientCommand = String.Format("iperf.exe -c {{0}} -p {0} -t {1} -fm -i 5 -B {{1}}", testIperfPort.Value, testIperfTimeToTransmit.Value);
			String testUniqId = "iperf/" + DateTime.Now.ToString("HHmmssff");

			if (testIperfUDP.Checked) {
				serverCommand += " -u";
				clientCommand += " -u -b " + testIperfUDPBandwidth.Value + "M";
			} else {
				String windowSizeCmd = " -w " +testIperfTcpWindowSize.Value + "K";
				serverCommand += windowSizeCmd;
				clientCommand += windowSizeCmd;
			}

			lock (nodeList) {
				int clientConnections = 0;
				int serverConnections = 0;

				if (testControlTypePeerToPeer.Checked && nodeList.SelectedItems.Count < 2) {
					MessageBox.Show("To perform p2p iperf test you need minimum 2 nodes", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
					return;
				}

				if (testControlTypeExternal.Checked) {
					servers.Add(Guid.Empty, testControlTypeExternalAddress.Text);
				} else if (testControlTypeEveryoneToOne.Checked) {
					Node node = (Node) testControlTypeEveryoneToOneNode.SelectedItem;
					servers.Add(node.Name, node.Ip.ToString());
				} else {
					double serverProbabilty = ((double) testIperfServers.Value / nodeList.SelectedItems.Count);
					for (int i = 0; i < nodeList.SelectedItems.Count; ++i) {
						ListViewItem node = nodeList.SelectedItems[i];
						if (servers.Count < testIperfServers.Value && ((new Random()).NextDouble() <= serverProbabilty || (testIperfServers.Value - servers.Count) >= nodeList.SelectedItems.Count - i)) {
							Guid nodeGuid = (Guid) node.Tag;
							try {
								String ip = Server.Instance.GetNode(nodeGuid).Ip.ToString();
								servers.Add(nodeGuid, ip);
							} catch (IndexOutOfRangeException) {
								Logger.Warning("Can\'t add server to server list!");
							}
						}
					}
				}

				serverConnections = servers.Count;
				testIperfStart.Enabled = false;

				// starting servers:
				foreach (KeyValuePair<Guid, String> server in servers) {
					backgroundWorker = new BackgroundWorker();
					backgroundWorker.DoWork += (o, ea) => {
						KeyValuePair<Guid, String> serverToBind = (KeyValuePair<Guid, String>) ea.Argument;
						if (serverToBind.Key.Equals(Guid.Empty)) {
							return;
						}
						try {
							WifiConnectionState serverState = Server.Instance.NodeControl(serverToBind.Key).WifiGetConnectionState();
							if (null != serverState.ConnectionIPv4UnicastMask && serverState.InterfaceState.Equals("Connected") && serverState.ConnectionOperationalStatus.Equals("Up")) {
								IPAddress ip = new IPAddress(serverState.ConnectionIPv4UnicastAddress);
								if (!ip.Equals(IPAddress.None)) {
									serverToBind = new KeyValuePair<Guid, string>(serverToBind.Key, ip.ToString());
									servers[serverToBind.Key] = ip.ToString();
								}
							}
							Logger.Info(String.Format("Starting server on node {1} [#{0}]", serverToBind.Key, serverToBind.Value));
							Server.Instance.NodeControl(serverToBind.Key).StartCustomTestServer(String.Format(serverCommand, serverToBind.Value), 0);
						} catch (IndexOutOfRangeException) {
							Logger.Warning("Can\'t start iperf server!");
						}
					};

					backgroundWorker.RunWorkerCompleted += (o, ea) => {
						if (serverConnections < 1) {
							serverConnections = 1;
						}
						if (0 == Interlocked.Decrement(ref serverConnections)) {
							// all servers started
							// starting clients
							Logger.Info("All servers stand in by!");
							clientConnections = nodeList.SelectedItems.Count;
							for (int i = 0; i < nodeList.SelectedItems.Count; ++i) {
								ListViewItem node = nodeList.SelectedItems[i];

								KeyValuePair<Guid, String> clientNode = new KeyValuePair<Guid, String>((Guid) node.Tag, Server.Instance.GetNode((Guid) node.Tag).Ip.ToString());
								KeyValuePair<Guid, String> serverNode = servers.ElementAt(i % servers.Count);

								if (clientNode.Key.Equals(serverNode.Key)) {
									serverNode = servers.ElementAt((i + 1) % servers.Count);
								}
								if (!testIperfServersAreClient.Checked && servers.Count(n => n.Key.Equals(clientNode.Key)) > 0) {
									Interlocked.Decrement(ref clientConnections);
									continue;
								}

								bw = new BackgroundWorker();

								bw.DoWork += (obj, eargs) => {
									Object[] clientAndServerObjectArray = (Object[]) eargs.Argument;
									KeyValuePair<Guid, String> client2 = (KeyValuePair<Guid, String>) clientAndServerObjectArray[0];
									KeyValuePair<Guid, String> server2 = (KeyValuePair<Guid, String>) clientAndServerObjectArray[1];

									Logger.Test(String.Format("Iperf request from node with IP {0} [#{1}] to {2} [#{3}]", client2.Value, client2.Key.ToString(), server2.Value, server2.Key.ToString()));
									try {
										WifiConnectionState clientState = Server.Instance.NodeControl(client2.Key).WifiGetConnectionState();
										if (null != clientState.ConnectionIPv4UnicastAddress && clientState.InterfaceState.Equals("Connected") && clientState.ConnectionOperationalStatus.Equals("Up")) {
											IPAddress ip = new IPAddress(clientState.ConnectionIPv4UnicastAddress);
											if (!ip.Equals(IPAddress.None)) {
												client2 = new KeyValuePair<Guid, string>(client2.Key, ip.ToString());
											}
										}
										Server.Instance.NodeControl(client2.Key).TestCustomTest(
											testUniqId,
											String.Format(clientCommand, server2.Value, client2.Value),
											0
										);
									} catch (IndexOutOfRangeException) {
										Logger.Critical(String.Format("Node {0} doesn't exist!", client2.Key));
									}
								};

								bw.RunWorkerCompleted += (obj, eargs) => {
									if (clientConnections < 1) {
										clientConnections = 1;
									}
									if (0 == Interlocked.Decrement(ref clientConnections)) {
										BackgroundWorker bw2;
										serverConnections = servers.Count;
										// zastopuj wszystkie serwery
										Logger.Debug("Stopping all iperf servers");
										foreach (KeyValuePair<Guid, String> serverToStop in servers) {
											bw2 = new BackgroundWorker();
											bw2.DoWork += (objS, eaS) => {
												if (serverToStop.Key.Equals(Guid.Empty)) {
													return;
												}
												try {
													Logger.Info(String.Format("Shutting down iperf server {0}", (Guid) eaS.Argument));
													Server.Instance.NodeControl((Guid) eaS.Argument).StopCustomTestServer();
												} catch (IndexOutOfRangeException) {
													Logger.Critical(String.Format("Node {0} doesn't exist!", (Guid) eaS.Argument));
												}
											};

											bw2.RunWorkerCompleted += (objS, eaS) => {
												if (0 == Interlocked.Decrement(ref serverConnections)) {
													testIperfStart.Enabled = true;
												}
											};

											bw2.RunWorkerAsync(serverToStop.Key);
										}
									}
								};

								Logger.Info("Let's cook something...");
								try {
									bw.RunWorkerAsync(new Object[] {
										clientNode,
										serverNode
									});
								} catch (IndexOutOfRangeException) {
									Logger.Warning("Can\'t start iperf server!");
								}
							}
						}
					};

					backgroundWorker.RunWorkerAsync(server);
				}
			}
		}

		private void testIperfUDP_CheckedChanged(object sender, EventArgs e) {
			testIperfUDPBandwidth.Enabled = testIperfUDP.Checked;
			testIperfTcpWindowSize.Enabled = !testIperfUDP.Checked;
		}

		private void testControlTypePeerToPeer_CheckedChanged(object sender, EventArgs e) {
			testIperfServers.Enabled = testIperfServersAreClient.Enabled = testControlTypePeerToPeer.Checked;
			testIperfServers_ValueChanged(testIperfServers, EventArgs.Empty);
		}

		private void testIperfServers_ValueChanged(object sender, EventArgs e) {
			testIperfServersCount.Text = testIperfServers.Value.ToString();

			if (testIperfServers.Value == 1) {
				testIperfServersAreClient.Checked = false;
				testIperfServersAreClient.Enabled = false;
			} else if (testIperfServers.Value > nodeList.SelectedIndices.Count / 2) {
				testIperfServersAreClient.Checked = true;
				testIperfServersAreClient.Enabled = false;
			} else {
				testIperfServersAreClient.Enabled = true;
			}
		}

		private void statusControlCloseNodes_Click(object sender, EventArgs e) {
			BackgroundWorker backgroundWorker;

			statusControlCloseNodes.Enabled = false;

			lock (nodeList) {
				int connections = nodeList.SelectedItems.Count;

				foreach (ListViewItem node in nodeList.SelectedItems) {
					backgroundWorker = new BackgroundWorker();
					backgroundWorker.DoWork += (o, ea) => {
						Logger.Info(String.Format("Shutting down node {0}", (Guid) ea.Argument));
						try {
							Server.Instance.NodeControl((Guid) ea.Argument).Shutdown();
						} catch (IndexOutOfRangeException) {
							Logger.Critical(String.Format("Node {0} doesn't exist!", (Guid) ea.Argument));
						}
					};

					backgroundWorker.RunWorkerCompleted += (o, ea) => {
						if (0 == Interlocked.Decrement(ref connections)) {
							statusControlCloseNodes.Enabled = true;
						}
					};

					backgroundWorker.RunWorkerAsync(node.Tag);
				}
			}
		}
	}
}
